#include <fmt/core.h>
#include <rapidjson/document.h>
#include <rapidjson/filewritestream.h>
#include <rapidjson/prettywriter.h>
#include <rapidjson/schema.h>
#include <rapidjson/writer.h>

#include "cppguide/util/log_util.h"
#include "doctest/doctest.h"
#include "testhelper.hpp"

TEST_SUITE("test_rapidjson") {
    TEST_CASE("test_rapidjson_read") {
        std::shared_ptr<spdlog::logger> log = cppguide::LogUtil::getLogger("test");
        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_read start"));

        std::string jsonPath = "plugins.json";
        auto        r        = TestHelper::getInstance()->readTextFile(jsonPath);
        CHECK_MESSAGE(r.ok(), fmt::format("文件:{} 打开失败", jsonPath));
        CHECK_MESSAGE(!r.data.empty(), fmt::format("文件:{} 内容为空", jsonPath));

        rapidjson::Document doc;
        doc.Parse(r.data.c_str());
        CHECK_MESSAGE(!doc.HasParseError(), fmt::format("文件:{} 解析失败", jsonPath));
        TestHelper::getInstance()->printJson(doc, true);

        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_read end"));
    }

    TEST_CASE("test_rapidjson_schema") {
        std::shared_ptr<spdlog::logger> log = cppguide::LogUtil::getLogger("test");
        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_schema start"));

        // 解析 schema 文件
        std::string schemaPath = "plugin_desc.schema.json";
        auto        schemar    = TestHelper::getInstance()->readTextFile(schemaPath);
        CHECK_MESSAGE(schemar.ok(), fmt::format("文件:{} 打开失败", schemaPath));
        CHECK_MESSAGE(!schemar.data.empty(), fmt::format("文件:{} 内容为空", schemaPath));
        rapidjson::Document schemaDoc;
        schemaDoc.Parse(schemar.data.c_str());
        CHECK_MESSAGE(!schemaDoc.HasParseError(), fmt::format("文件:{} 解析失败", schemaPath));
        rapidjson::SchemaDocument schema(schemaDoc);

        // 解析 json 文件
        std::string jsonPath = "janna.taskbuttons.json";
        auto        jsonr    = TestHelper::getInstance()->readTextFile(jsonPath);
        CHECK_MESSAGE(jsonr.ok(), fmt::format("文件:{} 打开失败", jsonPath));
        CHECK_MESSAGE(!jsonr.data.empty(), fmt::format("文件:{} 内容为空", jsonPath));
        rapidjson::Document jsonDoc;
        jsonDoc.Parse(jsonr.data.c_str());
        CHECK_MESSAGE(!jsonDoc.HasParseError(), fmt::format("文件:{} 解析失败", jsonPath));

        // schema 校验
        rapidjson::SchemaValidator validator(schema);
        CHECK_MESSAGE(jsonDoc.Accept(validator), fmt::format("文件:{} 验证失败", jsonPath));

        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_schema end"));
    }

    TEST_CASE("test_rapidjson_pointer") {
        std::shared_ptr<spdlog::logger> log = cppguide::LogUtil::getLogger("test");
        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_pointer start"));

        /* 解析 json 文件 */
        std::string jsonPath = "janna.taskbuttons.json";
        auto        jsonr    = TestHelper::getInstance()->readTextFile(jsonPath);
        CHECK_MESSAGE(jsonr.ok(), fmt::format("文件:{} 打开失败", jsonPath));
        CHECK_MESSAGE(!jsonr.data.empty(), fmt::format("文件:{} 内容为空", jsonPath));
        rapidjson::Document jsonDoc;
        jsonDoc.Parse(jsonr.data.c_str());
        CHECK_MESSAGE(!jsonDoc.HasParseError(), fmt::format("文件:{} 解析失败", jsonPath));

        /* pointer */
        // 获取id
        auto idValue = rapidjson::Pointer("/id").Get(jsonDoc);
        if (idValue && idValue->IsString()) {
            SPDLOG_LOGGER_INFO(log, fmt::format("id:{}", idValue->GetString()));
        } else {
            SPDLOG_LOGGER_ERROR(log, fmt::format("获取id失败"));
        }

        // 获取 subscribe
        auto subscribeValue = rapidjson::Pointer("/subscribe").Get(jsonDoc);
        if (!subscribeValue || !subscribeValue->IsArray()) {
            SPDLOG_LOGGER_ERROR(log, fmt::format("获取 subscribe 失败"));
        }
        // 循环输出
        if (!subscribeValue->Empty()) {
            auto subscribeList = subscribeValue->GetArray();
            for (rapidjson::SizeType i = 0; i < subscribeList.Size(); i++) {
                auto& el          = subscribeList[i];
                auto  sourceValue = rapidjson::Pointer("/source").Get(el);
                if (sourceValue && sourceValue->IsString()) {
                    SPDLOG_LOGGER_INFO(log, fmt::format("source:{}", sourceValue->GetString()));
                }
            }
        }

        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_pointer end"));
    }

    TEST_CASE("test_rapidjson_edit") {
        std::shared_ptr<spdlog::logger> log = cppguide::LogUtil::getLogger("test");
        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_edit start"));

        /* 解析 json 文件 */
        std::string jsonPath = "janna.taskbuttons.json";
        auto        jsonr    = TestHelper::getInstance()->readTextFile(jsonPath);
        CHECK_MESSAGE(jsonr.ok(), fmt::format("文件:{} 打开失败", jsonPath));
        CHECK_MESSAGE(!jsonr.data.empty(), fmt::format("文件:{} 内容为空", jsonPath));
        rapidjson::Document jsonDoc;
        jsonDoc.Parse(jsonr.data.c_str());
        CHECK_MESSAGE(!jsonDoc.HasParseError(), fmt::format("文件:{} 解析失败", jsonPath));

        /* pointer */
        // 获取 id
        auto idValue = rapidjson::Pointer("/id").Get(jsonDoc);
        if (idValue && idValue->IsString()) {
            SPDLOG_LOGGER_INFO(log, fmt::format("id:{}", idValue->GetString()));
        } else {
            SPDLOG_LOGGER_ERROR(log, fmt::format("获取id失败"));
        }

        // 修改 id
        idValue->SetString("janna.core:1.0.0");

        // subscribe 添加一项
        // 获取 subscribe
        auto subscribeValue = rapidjson::Pointer("/subscribe").Get(jsonDoc);
        if (!subscribeValue || !subscribeValue->IsArray()) {
            SPDLOG_LOGGER_ERROR(log, fmt::format("获取 subscribe 失败"));
        }

        // 创建 subscribe 子项
        rapidjson::Value anotherSubscribe(rapidjson::kObjectType);
        anotherSubscribe.AddMember("source", "janna", jsonDoc.GetAllocator());
        anotherSubscribe.AddMember("topic", "echo-core", jsonDoc.GetAllocator());
        subscribeValue->GetArray().PushBack(anotherSubscribe, jsonDoc.GetAllocator());

        // 输出
        TestHelper::getInstance()->printJson(jsonDoc, true);

        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_edit end"));
    }

    TEST_CASE("test_rapidjson_write") {
        std::shared_ptr<spdlog::logger> log = cppguide::LogUtil::getLogger("test");
        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_write start"));

        /* 解析 json 文件 */
        std::string jsonPath = "janna.taskbuttons.json";
        auto        jsonr    = TestHelper::getInstance()->readTextFile(jsonPath);
        CHECK_MESSAGE(jsonr.ok(), fmt::format("文件:{} 打开失败", jsonPath));
        CHECK_MESSAGE(!jsonr.data.empty(), fmt::format("文件:{} 内容为空", jsonPath));
        rapidjson::Document jsonDoc;
        jsonDoc.Parse(jsonr.data.c_str());
        CHECK_MESSAGE(!jsonDoc.HasParseError(), fmt::format("文件:{} 解析失败", jsonPath));

        /* pointer */
        // 获取 id
        auto idValue = rapidjson::Pointer("/id").Get(jsonDoc);
        if (idValue && idValue->IsString()) {
            SPDLOG_LOGGER_INFO(log, fmt::format("id:{}", idValue->GetString()));
        } else {
            SPDLOG_LOGGER_ERROR(log, fmt::format("获取id失败"));
        }

        // 修改 id
        idValue->SetString("janna.core:1.0.0");

        // subscribe 添加一项
        // 获取 subscribe
        auto subscribeValue = rapidjson::Pointer("/subscribe").Get(jsonDoc);
        if (!subscribeValue || !subscribeValue->IsArray()) {
            SPDLOG_LOGGER_ERROR(log, fmt::format("获取 subscribe 失败"));
        }

        // 创建 subscribe 子项
        rapidjson::Value anotherSubscribe(rapidjson::kObjectType);
        anotherSubscribe.AddMember("source", "janna", jsonDoc.GetAllocator());
        anotherSubscribe.AddMember("topic", "echo-core", jsonDoc.GetAllocator());
        subscribeValue->GetArray().PushBack(anotherSubscribe, jsonDoc.GetAllocator());

        // 输出
        FILE* fp = fopen(jsonPath.c_str(), "w");  // 非 windows 平台使用 "w", windows 平台使用 "wb"
        CHECK_MESSAGE(fp, fmt::format("文件:{} 创建失败", jsonPath));
        char                                                writeBuffer[65536];
        rapidjson::FileWriteStream                          os(fp, writeBuffer, sizeof(writeBuffer));
        rapidjson::PrettyWriter<rapidjson::FileWriteStream> writer(os);
        jsonDoc.Accept(writer);
        fclose(fp);

        SPDLOG_LOGGER_INFO(log, fmt::format("test_rapidjson_write end"));
    }
}